/*************************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2009.
-------------------------------------------------------------------------
$Id$
$DateTime$
Description: Implements a visibility determination map for use with AI.

-------------------------------------------------------------------------
History:
- 4:3:2009   11:38 : Created by Marcio Martins

*************************************************************************/
#ifndef __VisionMap_h__
#define __VisionMap_h__

#pragma once


#include "PoolAllocator.h"
#include "STLPoolAllocator.h"
#include "STLPoolAllocator_ManyElems.h"
#include <HashGrid.h>

#include <IEntitySystem.h>
#include <IVisionMap.h>


// TODO:	Allow 360 degrees vision

class	CVisionMap :
	public IVisionMap,
	public IEntitySystemSink
{
	static const float PositionEpsilon;
	static const float OrientationEpsilon;
public:
	CVisionMap();
	virtual ~CVisionMap();

	// IEntitySystemSink
	virtual bool OnBeforeSpawn(SEntitySpawnParams& params);
	virtual void OnSpawn(IEntity* pEntity,SEntitySpawnParams& params);
	virtual bool OnRemove(IEntity* pEntity);
	virtual void OnReused(IEntity *pEntity, SEntitySpawnParams &params);
	virtual void OnEvent(IEntity* pEntity, SEntityEvent& event);
	//~IEntitySystemSink

	virtual void Reset();

	virtual VisionID CreateVisionID(const char* name);

	virtual void RegisterObserver(const ObserverID& observerID, const ObserverParams& params);
	virtual void UnregisterObserver(const ObserverID& observerID);

	virtual void RegisterObservable(const ObservableID& observableID, const ObservableParams& params);
	virtual void UnregisterObservable(const ObservableID& observableID);

	virtual void ObserverChanged(const ObserverID& observerID, const ObserverParams& params, uint32 hint);
	virtual void ObservableChanged(const ObservableID& observableID, const ObservableParams& params, uint32 hint);

	virtual bool IsVisible(const ObserverID& observerID, const ObservableID& observableID) const;
	virtual const ObserverParams* GetObserverParams(const ObserverID& observerID) const;
	virtual const ObservableParams* GetObservableParams(const ObservableID& observableID) const;

	virtual void Update(float frameTime);

	void DebugDraw();
protected:
	struct ObservableInfo
	{
		ObservableInfo(const ObservableID& _id, const ObservableParams& _params)
			: id(_id)
			, params(_params)
		{
		};
		
		ObservableID id;
		ObservableParams params;
	};

	struct observable_position
	{
		inline Vec3 operator ()(const ObservableInfo* info) const
		{
			return info->params.pos[0];
		}
	};

	typedef hash_grid<256, ObservableInfo*, hash_grid_2d<Vec3, Vec3i>, observable_position> ObservablesGrid;
	typedef stl::hash_map<ObservableID, ObservableInfo, stl::hash_uint32> Observables;

	struct PVSEntry
	{
		PVSEntry(const ObservableInfo& _observableInfo)
			: pendingRayID(0)
			, observableInfo(_observableInfo)
			, visible(false)
			, currentPos(0)
		{
		};

		QueuedRayID pendingRayID;
		const ObservableInfo& observableInfo;

		bool visible		: 1;
		int8 currentPos	: 7;
	};

	typedef std::map<ObservableID, PVSEntry> PVS;

	struct ObserverInfo
	{
		ObserverInfo()
			: dirtyPVS(true)
			, dirtyVis(true)
			, pvsUpdated(0.0f)
			, visUpdated(0.0f)
		{
		};

		PVS pvs;
		ObserverParams params;

		CTimeValue pvsUpdated;
		CTimeValue visUpdated;

		bool dirtyPVS : 1;
		bool dirtyVis : 1;
	};

	typedef stl::hash_map<ObserverID, ObserverInfo, stl::hash_uint32> Observers;

	struct PendingRayInfo
	{
		PendingRayInfo(const ObserverID& _observerID, const ObserverInfo& _observerInfo, PVSEntry& _entry)
			: observerID(_observerID)
			, observerInfo(_observerInfo)
			, entry(_entry)
		{
		}

		ObserverID observerID;
		const ObserverInfo& observerInfo;

		PVSEntry& entry;
	};

	typedef std::map<QueuedRayID, PendingRayInfo> PendingRays;

protected:
	void AcquireSkipList(IPhysicalEntity** skipList, uint32 skipListSize);
	void ReleaseSkipList(IPhysicalEntity** skipList, uint32 skipListSize);

	void DebugDrawStats();

	void DebugDraw_ObserverPVS(const ObserverID& observerID, const ObserverInfo& observerInfo);
	void DebugDraw_ObserverStats(const ObserverID& observerID, const ObserverInfo& observerInfo);

	inline bool IsPointInFoV(const Vec3& eyePos, const Vec3& eyeDir, const Vec3& point, float fovCos, float distanceSq) const
	{
		// If we're too close, use a plane check instead of
		// cone check, so that AI won't miss things at their feet
		if (distanceSq < square(2.f))
		{
			Plane viewPlane = Plane::CreatePlane(eyeDir, eyePos);
			return viewPlane.DistFromPlane(point) > 0.f;
		}

		Vec3 targetDir = point - eyePos;
		targetDir.NormalizeFast();

		float dot = targetDir.Dot(eyeDir);
		return (dot >= fovCos);
	}

	inline bool IsPointInFront(const Vec3& eyePos, const Vec3& eyeDir, const Vec3& point) const
	{
		Plane viewPlane = Plane::CreatePlane(eyeDir, eyePos);
		return viewPlane.DistFromPlane(point) > 0.0f;
	}

	inline const Vec3& GetObservablePos(ObservableID observableID) const
	{
		Observables::const_iterator it = m_observables.find(observableID);
		assert(it != m_observables.end());
		return it->second.params.pos[0];
	}

	void UpdatePVS(const ObserverID& observerID, ObserverInfo& observerInfo, const CTimeValue& now);
	void UpdateVisibility(const ObserverID& observerID, ObserverInfo& observerInfo, const CTimeValue& now);
	
	QueuedRayID QueueRay(const ObserverID& observerID, const ObserverInfo& observerInfo, PVSEntry& entry);
	bool RayCastSubmit(const QueuedRayID& rayID, RayCastRequest& request);
	void RayCastComplete(const QueuedRayID& rayID, const RayCastResult& result);

	void DeletePendingRay(PVSEntry& entry);
	void DeletePendingRays(PVS& pvs);

	Observers	 m_observers;
	Observables m_observables;		
	ObservablesGrid m_observablesGrid;
	PendingRays m_pendingRays;

	typedef std::vector<std::pair<float, ObservableInfo*> > QueryObservables;
	QueryObservables m_queryObservables;

	uint32 m_pvsUpdateCount;
	uint32 m_visUpdateCount;
	
	uint32 m_genID;
};


#endif // __VisionMap_h__
